# -*- coding: utf-8 -*-
import json
import os
import datetime
import time
import copy
import slugify
import uuid
        

def init(**kwargs):
    pass
    
class Span(object):
    
    SPAN_LIST = []
    
    def __init__(self,span_id,name=None):
        self.span_id = span_id
        self.start_time = None
        self.end_time = None
        self.name = name
        self.parent_id = None
        self.logs = []
        self.tags = []
        self.context = Context(**{'span_id':span_id})
        self.SPAN_LIST.append(self)
    
    @classmethod  
    def get_span(cls,span_id):
        for span in cls.SPAN_LIST:
            if span.span_id == span_id:
                return span
        raise RuntimeError("span id %s is not exist"%span_id)
        
    @classmethod  
    def get_context(cls,span_id):
        for span in cls.SPAN_LIST:
            if span.context.span_id == span_id:
                return span.context
        raise RuntimeError("span id %s is not exist"%span_id)

def create_new_id():
    '''
    '''
    span = Span(str(uuid.uuid1()).replace("-",""))
    return span.span_id
    
def put_tag(span_id,key,value):
    '''
    '''
    span = Span.get_span(span_id)
    names = [key]
    values = [value]
    for tag in span.tags:
        tag_names = tag.name
        tag_values = tag.value
        if key == tag_names[0] and value == tag_values[0]:
            return
        elif key == tag_names[0]:
            i = 2
            while True:
                loop_key = "%s.%d"%(key,i)
                if loop_key not in tag_names:
                    tag_names.append(loop_key)
                    tag_values.insert(0,value)
                    tag.name = tag_names
                    tag.value = tag_values
                    is_new_tag = False
                    return
                i+=1
    tag = Tag(span_id=span_id,name=names,value=values)
    span.tags.append(tag)
    
def put_log(span_id,level,msg,rings = [],**kwargs):
    '''
    '''
    log = Log(span_id,level,msg)
    span = Span.get_span(span_id)
    log.payload = kwargs
    span.logs.append(log)
    for ring in rings:
        log.rings.append(Ring(name=ring))

def finish_span(span_id):
    '''
    '''
    span = Span.get_span(span_id)
    span.end_time = datetime.datetime.utcnow()
    
def start_span(span_id,parent_id=None,tags=[],context=None):
    '''
    '''
    span = Span.get_span(span_id)
    span.start_time = datetime.datetime.utcnow()
    if parent_id is not None:
        Span.get_span(parent_id)
        span.parent_id = parent_id
    for k,v in tags:
        put_tag(span_id,k,v)
        
    if context is not None:
        span.context = Context(**context)
    

def set_span_name(span_id,name):
    '''
    '''
    span = Span.get_span(span_id)
    span.name = name

class Log(object):
    def __init__(self,span_id,event,message):
        self.span_id = span_id
        self.event = event
        self.timestamp = datetime.datetime.utcnow()
        self.message = message
        self.payload = {}
        self.rings = []

class Tag(object):
    def __init__(self,span_id,name=[],value=[]):
        self.span_id = span_id
        self.name = name
        self.value = value

class Context(object):
    def __init__(self,**kwargs):
        self.span_id = kwargs.get('span_id')
        self.parent_id = kwargs.get('parent_id',None)
        self.baggage = kwargs.get('baggage',{})
        self.flags = kwargs.get('flags',0)
        self.trace_id = kwargs.get('trace_id')
    

class Ring(object):
    def __init__(self,name):
        self.name = name
    
def obj_to_dict(obj):
    return dict(obj.to_mongo())
    
def to_list_dict(objs):
    lst = []
    for obj in objs:
        lst.append(obj_to_dict(obj))
    return lst

def full_span_id(span_id):
    parent_id_list = [str(span_id)]
    parent_id = span_id
    while parent_id:
        span = Span.objects(id=parent_id).first()
        parent_id = span.parent_id
        if parent_id:
            parent_id_list.append(str(parent_id))
    parent_id_list.reverse()
    return ".".join(parent_id_list)

def get_span_info(span_id,contain_child=False,to_dict=True):
    span = Span.get_span(span_id)
    tags = Tag.objects(span_id=span_id)
    if to_dict:
        span_data = obj_to_dict(span)
        span_data['id'] = full_span_id(span_id)
        tag_list = to_list_dict(tags)
        span_data['tags'] = tag_list
        span_data['childs'] = []
        if contain_child:
            child_spans = Span.objects(parent_id=span_id)
            childs = []
            for child_span in child_spans:
                childs.append(get_span_info(child_span.id,contain_child,to_dict))
            span_data['childs'] = childs
            
        context = Context.objects(span_id=span_id).first()
        if not context:
            span_data['context'] = None
        else:
            span_data['context'] = obj_to_dict(context)
        return span_data
    return span

def get_span_logs(span_id,timestamp=None,log_ring=None,recursive=False,log_list = []):
    q = {
        'span_id':span_id
    }
    if timestamp is not None:
        q["timestamp__gt"] = timestamp
    logs = Log.objects(**q)
    if log_ring is not None:
        ring_logs = []
        for log in logs:
            if Ring.objects(name=log_ring,log_id=log.id).first():
                ring_logs.append(log)
        logs = ring_logs
    
    log_list.extend(to_list_dict(logs))
    if recursive:
        child_span_ids = Span.objects(parent_id=span_id).only("id")
        for child_span_id in child_span_ids:
            get_span_logs(child_span_id.id,timestamp,log_ring,recursive,log_list)

def form_span_id(span_id):
    obj_span_id = hex(span_id).replace("0x","").replace("L","")
    return obj_span_id
    
def update_context(context):
    obj = Span.get_context(span_id=context['span_id'])
    obj.baggage = context['baggage']
    obj.flags = context['flags']
    
def get_span_context(span_id):
    context = Context.objects(span_id=span_id).first()
    if not context:
        return None
    return obj_to_dict(context)
